package com.radar.io.cinrad;

import cn.hutool.core.date.DateUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.radar.algorithm.MathCommon;
import com.radar.common.CinradType;
import com.radar.common.GisConstants;
import com.radar.common.ProductType;
import com.radar.exception.RadarDecodeError;
import com.radar.utils.ArraysExtent;
import com.radar.utils.BitConverter;
import ucar.ma2.*;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.Variable;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.IntStream;

/**
 * @author: fy-wbj
 * @description: CA CB波段 雷达
 * @date: 2020/10/10
 */
public class RadarCAB extends BaseCinrad {

    private double[] ele;

    private double[][] azi;

    private double rreso;

    private double vreso;

    public RadarCAB(String file, CinradType type, String code) {
        super(file, type, code);
    }

    static Log log = LogFactory.get();

    public void parse(byte[] bytes) throws RadarDecodeError {
        RadarHeader h = new RadarHeader();
        int nSize = bytes.length;
        if (nSize % h.len != 0) {
            throw new RadarDecodeError("cab 数据解析异常");
        }
        try {
            int index = 0;
            int totalrecords = (nSize / h.len);
            byte[] bytdata = new byte[h.len];
            List<RadarHeader> headers = new ArrayList<>();
            List<Integer> boundIndex = new ArrayList<>();
            for (int i = 0; i < totalrecords; i++) {
                RadarHeader header = new RadarHeader();
                System.arraycopy(bytes, index, bytdata, 0, bytdata.length);
                header.parse(bytdata);
                index += bytdata.length;
                headers.add(header);
                if (header.radialNumber == 1) {
                    boundIndex.add(i);
                }
            }
            long sec = ((headers.get(0).julianDate - 1) * 86400) + headers.get(0).mSeconds / 1000;
            Date scanTime = DateUtil.offsetSecond(DateUtil.parse("1970-01-01").toJdkDate(), (int) sec).toJdkDate();
            this.vcp = headers.get(0).vcpNumber;
            int layers = boundIndex.size();
            double[] el = new double[layers];
            double[] azimuth = new double[layers];
            double[] nyquist_v = new double[layers];
            int[] gnr = new int[layers];
            int[] gnv = new int[layers];
            int[] records = new int[layers];
            int zindex = 0;
            for (int id : boundIndex) {
                el[zindex] = headers.get(id).el / 8.0 * (180.0 / 4096.0);
                azimuth[zindex] = headers.get(id).az / 8.0 * (180.0 / 4096.0);
                nyquist_v[zindex] = headers.get(id).nyquist / 100.0;
                if (zindex == layers - 1) {
                    records[zindex] = headers.size() - boundIndex.get(zindex);
                } else {
                    records[zindex] = boundIndex.get(zindex + 1) - boundIndex.get(zindex);
                }
                gnr[zindex] = headers.get(id).gatesNumberOfReflectivity;
                gnv[zindex] = headers.get(id).gatesNumberOfDoppler;
                zindex++;
            }
            // 数组定长
            int recordMax = Arrays.stream(records).max().getAsInt();
            byte[][][] z = new byte[layers][recordMax][800];
            byte[][][] v = new byte[layers][recordMax][1600];
            byte[][][] w = new byte[layers][recordMax][1600];
            byte[][][] rf = new byte[layers][recordMax][1600];
            double[][] azi = new double[layers][recordMax];
            int dv = headers.get(0).resolutionOfVelocity;
            index = 0;
            int layerIndex = 0;
            for (int i = 0; i < layers; i++) {
                MathCommon.fill(z[layerIndex]);
                MathCommon.fill(v[layerIndex]);
                MathCommon.fill(w[layerIndex]);
                MathCommon.fill(azi[layerIndex]);
                int bidx = records[i];
                int rnum = gnr[i];
                int vnum = gnv[i];
                for (int j = 0; j < bidx; j++) {
                    // header
                    index += 128;
                    byte[] ref = Arrays.copyOfRange(bytes, index, index + rnum);
                    for (int k = 0; k < rnum; k++) {
                        z[i][j][k] = MathCommon.CodeRef(decodeRef((0xff & ref[k])));
                    }
                    index += rnum;
                    byte[] vel = Arrays.copyOfRange(bytes, index, index + vnum);
                    for (int k = 0; k < vnum; k++) {
                        v[i][j][k] = MathCommon.CodeV(decodeVel((0xff & vel[k]), dv));
                        rf[i][j][k] = MathCommon.CodeV(decodeRf((0xff & vel[k])));
                    }
                    index += vnum;
                    byte[] sw = Arrays.copyOfRange(bytes, index, index + vnum);
                    index += vnum;
                    for (int k = 0; k < vnum; k++) {
                        w[i][j][k] = MathCommon.CodeV(decodeSpw((0xff & sw[k])));
                    }
                    int bindex = (boundIndex.get(i) + j);
                    azi[i][j] = headers.get(bindex).az / 8.0 * (180.0 / 4096.0);
                    index += h.len - 128 - rnum - vnum * 2;
                }
                layerIndex++;
            }
            // set GlobalAttr
            this.dateTime = scanTime;
            this.ele = el;
            this.azi = azi;
            rreso = headers.get(0).gateSizeOfReflectivity;
            vreso = headers.get(0).gateSizeOfDoppler;
            this.initNetcdfTemp();
            this.writeBasicData(z, v, w, rf, nyquist_v);
            // set range
        } catch (Exception e) {
            log.error(e);
            throw new RadarDecodeError("cab 数据解析异常");
        }
    }

    @Override
    public double[] getElevation() {
        return ele;
    }

    @Override
    public int[] getElevationIndex(ProductType type) {
        if (type == ProductType.REF) {
            return IntStream.range(0, ele.length).filter(i -> i != 1 && i != 3).toArray();
        } else {
            return IntStream.range(0, ele.length).filter(i -> i != 0 && i != 2).toArray();
        }
    }

    @Override
    public double[][] getAzimuth() {
        return azi;
    }

    @Override
    public double getResolution(ProductType type) {
        if (ProductType.REF.equals(type)) {
            return rreso;
        } else {
            return vreso;
        }
    }

    public void writeBasicData(byte[][][] z, byte[][][] v, byte[][][] w, byte[][][] rf, double[] nyquist_v) throws IOException, InvalidRangeException {
        int dlength = z[0][0].length;
        int vlength = v[0][0].length;
        write.setRedefineMode(true);
        Variable v1;
        Variable v2;
        if ((v1 = this.netcdf.findVariable("r_distance")) == null) {
            write.addDimension(null, "r_distance", dlength);
            v1 = write.addVariable(null, "r_distance", DataType.DOUBLE, "r_distance");
            v1.addAttribute(new Attribute("units", "m"));
            v1.addAttribute(new Attribute("resolution", rreso));
        }
        if ((v2 = this.netcdf.findVariable("v_distance")) == null) {
            write.addDimension(null, "v_distance", vlength);
            v2 = write.addVariable(null, "v_distance", DataType.DOUBLE, "v_distance");
            v2.addAttribute(new Attribute("units", "m"));
            v2.addAttribute(new Attribute("resolution", vreso));
        }
        List<Dimension> rdimensions = new ArrayList<>();
        rdimensions.add(netcdf.findDimension("elevation"));
        rdimensions.add(netcdf.findDimension("azimuth"));
        rdimensions.add(netcdf.findDimension("r_distance"));

        List<Dimension> vdimensions = new ArrayList<>();
        vdimensions.add(netcdf.findDimension("elevation"));
        vdimensions.add(netcdf.findDimension("azimuth"));
        vdimensions.add(netcdf.findDimension("v_distance"));

        Variable rVariable = ProductType.REF.writeVariable(write, netcdf, rdimensions);
        Variable vVariable = ProductType.VEL.writeVariable(write, netcdf, vdimensions);
        Variable wVariable = ProductType.SW.writeVariable(write, netcdf, vdimensions);
        Variable rfVariable = ProductType.RF.writeVariable(write, netcdf, vdimensions);

        // 不模糊速度
        Variable v3;
        if ((v3 = this.netcdf.findVariable("NYQUIST_V")) == null) {
            v3 = write.addVariable(null, "NYQUIST_V", DataType.DOUBLE, "elevation");
            v3.addAttribute(new Attribute("missing_value", GisConstants.UNDEF));
        }
        ArrayDouble rDistances = new ArrayDouble.D1(dlength);
        ArrayDouble vDistances = new ArrayDouble.D1(vlength);

        IntStream.range(0, dlength).forEach(i -> rDistances.setDouble(i, (i + 1) * rreso));
        IntStream.range(0, vlength).forEach(i -> vDistances.setDouble(i, (i + 1) * vreso));

        ArrayByte rArray = MathCommon.toByteArray(z);
        ArrayByte wArray = MathCommon.toByteArray(w);
        ArrayByte vArray = MathCommon.toByteArray(v);
        ArrayByte rfArray = MathCommon.toByteArray(rf);

        rVariable.setCachedData(rArray);
        vVariable.setCachedData(vArray);
        wVariable.setCachedData(wArray);
        rfVariable.setCachedData(rfArray);

        v1.setCachedData(rDistances);
        v2.setCachedData(vDistances);
        ArrayDouble a3 = new ArrayDouble.D1(ele.length);
        IntStream.range(0, ele.length).forEach(i -> a3.setDouble(i, nyquist_v[i]));

        v3.setCachedData(a3);
    }

    //region 原始数据文件

    public class RadarHeader {
        public int len = 4132;
        private String temp1;                        //保留
        private int radarStatus;                    //1 - 表示为雷达数据
        private String temp2;                        //保留
        private int mSeconds;                        //径向数据收集时间
        private int julianDate;                        //从1970/1/1起的日期
        private int uRange;                            //不模糊距离
        private int az;                                //方位角度
        private int radialNumber;                    //径向数据序号
        private int radialStatus;                    //径向数据状态
        private int el;                                //仰角
        private int elNumber;                        //体扫内的仰角编号
        private int rangeToFirstGateOfRef;            //第一个反射率数据表示的实际距离(m)
        private int rangeToFirstGateOfDop;            //第一个多普勒数据表示的实际距离(m)
        private int gateSizeOfReflectivity;            //反射率数据的距离库长(m)
        private int gateSizeOfDoppler;                //多普勒数据的距离库长(m)
        private int gatesNumberOfReflectivity;        //反射率数据的距离库数
        private int gatesNumberOfDoppler;            //多普勒数据的距离库数
        private int cutSectorNumber;                //扇区号
        private int calibrationConst;                //标定常数
        private int ptrOfReflectivity;                //反射率数据指针
        private int ptrOfVelocity;                    //速度数据指针
        private int ptrOfSpectrumWidth;                //谱宽数据指针
        private int resolutionOfVelocity;            //多普勒速度分辨率
        private int vcpNumber;                        //体扫号
        private String temp4;                        //保留
        private int ptrOfArcReflectivity;            //反射率数据指针
        private int ptrOfArcVelocity;                //速度数据指针
        private int ptrOfArcWidth;                    //谱宽数据指针
        private int nyquist;                        //不模糊速度
        private int temp46;                         //保留
        private int temp47;                         //保留
        private int temp48;                         //保留
        private int circleTotal;                    //仰角数
        private String temp5;                        //保留
        private byte[] z;
        private byte[] v;
        private byte[] w;
        private String temp;                        //保留

        public void parse(byte[] b) throws Exception {
            temp1 = BitConverter.toString(Arrays.copyOfRange(b, 0, 14));
            int index = 14;
            radarStatus = BitConverter.toInt16(b, index);
            index += 2;
            temp2 = BitConverter.toString(Arrays.copyOfRange(b, index, index + 12));
            index += 12;
            mSeconds = BitConverter.toInt32(b, index);
            index += 4;
            julianDate = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            uRange = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            az = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            radialNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            radialStatus = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            el = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            elNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            rangeToFirstGateOfRef = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            rangeToFirstGateOfDop = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            gateSizeOfReflectivity = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            gateSizeOfDoppler = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            gatesNumberOfReflectivity = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            gatesNumberOfDoppler = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            cutSectorNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            calibrationConst = BitConverter.toInt32(b, index);
            index += 4;
            ptrOfReflectivity = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            ptrOfVelocity = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            ptrOfSpectrumWidth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            resolutionOfVelocity = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            vcpNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            temp4 = BitConverter.toString(Arrays.copyOfRange(b, index, index + 8));
            index += 8;
            ptrOfArcReflectivity = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            ptrOfArcVelocity = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            ptrOfArcWidth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            nyquist = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            temp46 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            temp47 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            temp48 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            circleTotal = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            temp5 = BitConverter.toString(Arrays.copyOfRange(b, index, index + 30));
            index += 30;
            //129－1508 共1380字节速度数据
            //129－2428 共2300字节谱宽数据
        }
    }

    //endregion
}
